package com.zlyx.easy.http.parser.defaults;

import java.lang.reflect.Method;

import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import com.zlyx.easy.core.utils.JsonUtils;
import com.zlyx.easy.core.utils.MethodUtils;
import com.zlyx.easy.core.utils.ObjectUtils;
import com.zlyx.easy.core.utils.StringUtils;
import com.zlyx.easy.http.annotations.HttpMethod;
import com.zlyx.easy.http.annotations.HttpProvider;
import com.zlyx.easy.http.annotations.HttpService;
import com.zlyx.easy.http.models.RequestModel;
import com.zlyx.easy.http.parser.HttpMethodParser;

/**
 * @Auth 赵光
 * @Describle
 * @2020年1月11日
 */
public class DefaultHttpMethodParser implements HttpMethodParser {

	@Override
	public RequestModel parse(Class<?> proxyClass, Method method, Object[] args) throws Exception {
		RequestModel rm = new RequestModel();
		handleAnnotation(proxyClass, method, args, rm);
		handleMapping(method, rm);
		rm.setCls(proxyClass);
		rm.setReturnType(method.getReturnType());
		return rm;
	}

	/**
	 * 处理注解
	 * 
	 * @param proxyClass 代理类
	 * @param method     代理方法
	 * @param args       参数
	 * @return
	 * @throws Exception
	 */
	public void handleAnnotation(Class<?> proxyClass, Method method, Object[] args, RequestModel rm) throws Exception {
		HttpService httpService = proxyClass.getAnnotation(HttpService.class);
		rm.setBaseUrl(httpService.url());
		rm.setName(httpService.name());
		Class<?> providerClass = httpService.providerClass();
		if (providerClass == Void.class) {
			rm.setParams(MethodUtils.getParamsMap(method, args));
			return;
		}
		HttpProvider httpProvider = method.getAnnotation(HttpProvider.class);
		String methodName = null;
		if (httpProvider != null) {
			methodName = StringUtils.getOrDefault(httpProvider.providerMethod(), method.getName());
			method = providerClass.getDeclaredMethod(methodName, method.getParameterTypes());
			Object httpParam = method.invoke(providerClass.getConstructor().newInstance(), args);
			rm.setParams(JsonUtils.toMap(httpParam)); // 支持字符串和Map类型
			return;
		}
		HttpMethod httpMethod = method.getAnnotation(HttpMethod.class);
		if (httpMethod != null) {
			methodName = StringUtils.getOrDefault(httpMethod.providerMethod(), method.getName());
			method = providerClass.getDeclaredMethod(methodName, method.getParameterTypes());
			Object httpParam = method.invoke(providerClass.getConstructor().newInstance(), args);
			rm.setParams(JsonUtils.toMap(httpParam)); // 支持字符串和Map类型
			return;
		}
	}

	/**
	 * 决断请求类型
	 * 
	 * @param method
	 * @param rm
	 */
	public void handleMapping(Method method, RequestModel rm) {
		if (handleHttpMethod(method, rm)) {
			return;
		}
		if (handleRequestMapping(method, rm)) {
			return;
		}
		if (handlePostMapping(method, rm)) {
			return;
		}
		if (handleGetMapping(method, rm)) {
			return;
		}
		if (handlePutMapping(method, rm)) {
			return;
		}
		if (handleDeleteMapping(method, rm)) {
			return;
		}
	}

	/**
	 * handleHttpMethod
	 * 
	 * @param method
	 * @param model
	 */
	public boolean handleHttpMethod(Method method, RequestModel model) {
		HttpMethod mapping = method.getAnnotation(HttpMethod.class);
		if (mapping != null) {
			model.setPath(mapping.value());
			model.setMediaType(mapping.consume());
			model.setMethod(mapping.method());
			return true;
		}
		return false;
	}

	/**
	 * handleRequestMapping
	 * 
	 * @param method
	 * @param model
	 * @return
	 */
	public boolean handleRequestMapping(Method method, RequestModel model) {
		RequestMapping mapping = method.getAnnotation(RequestMapping.class);
		if (mapping != null) {
			String[] values = mapping.value();
			if (ObjectUtils.isNotEmpty(values)) {
				model.setPath(values[0]);
			}
			String[] consumes = mapping.consumes();
			if (ObjectUtils.isNotEmpty(consumes)) {
				model.setMediaType(consumes[0]);
			}
			RequestMethod[] methods = mapping.method();
			if (methods != null && methods.length > 0) {
				model.setMethod(methods[0]);
			} else {
				model.setMethod(RequestMethod.POST);
			}
			return true;
		}
		return false;
	}

	/**
	 * handlePostMapping
	 * 
	 * @param method
	 * @param model
	 * @return
	 */
	public boolean handlePostMapping(Method method, RequestModel model) {
		PostMapping mapping = method.getAnnotation(PostMapping.class);
		if (mapping != null) {
			String[] values = mapping.value();
			if (ObjectUtils.isNotEmpty(values)) {
				model.setPath(values[0]);
			}
			String[] consumes = mapping.consumes();
			if (ObjectUtils.isNotEmpty(consumes)) {
				model.setMediaType(consumes[0]);
			}
			model.setMethod(RequestMethod.POST);
			return true;
		}
		return false;
	}

	/**
	 * handleGetMapping
	 * 
	 * @param method
	 * @param model
	 * @return
	 */
	public boolean handleGetMapping(Method method, RequestModel model) {
		GetMapping mapping = method.getAnnotation(GetMapping.class);
		if (mapping != null) {
			String[] values = mapping.value();
			if (ObjectUtils.isNotEmpty(values)) {
				model.setPath(values[0]);
			}
			model.setMethod(RequestMethod.GET);
			return true;
		}
		return false;
	}

	/**
	 * handlePutMapping
	 * 
	 * @param method
	 * @param model
	 * @return
	 */
	public boolean handlePutMapping(Method method, RequestModel model) {
		PutMapping mapping = method.getAnnotation(PutMapping.class);
		if (mapping != null) {
			String[] values = mapping.value();
			if (ObjectUtils.isNotEmpty(values)) {
				model.setPath(values[0]);
			}
			model.setMethod(RequestMethod.PUT);
			return true;
		}
		return false;
	}

	/**
	 * handlePutMapping
	 * 
	 * @param method
	 * @param model
	 * @return
	 */
	public boolean handleDeleteMapping(Method method, RequestModel model) {
		DeleteMapping mapping = method.getAnnotation(DeleteMapping.class);
		if (mapping != null) {
			String[] values = mapping.value();
			if (ObjectUtils.isNotEmpty(values)) {
				model.setPath(values[0]);
			}
			model.setMethod(RequestMethod.DELETE);
			return true;
		}
		return false;
	}

}
